package org.kernely.security.persistence;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

import org.hibernate.annotations.Cascade;

/**
 * Users of Kernely.
 */
@Entity
@Table(name="user",
		uniqueConstraints=@UniqueConstraint(columnNames="login"))
public class User {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name="user_id")
	/**
	 * User's id
	 */
	private int id;

	/**
	 * User's login
	 */
	private String login;

	/**
	 * User's password
	 */
	private String password;
	
	/**
	 * User's access to the application
	 */
	private int enabled;

	/**
	 * Groups of the user
	 */
    @ManyToMany(
            mappedBy = "users",
            fetch=FetchType.LAZY
        )
	private Set<Group> groups;
    
    /**
	 * Roles of the user
	 */
    @ManyToMany(fetch=FetchType.LAZY)
	@JoinTable( name="kernely_user_roles",
				joinColumns=@JoinColumn(name="fk_user"),
				inverseJoinColumns=@JoinColumn(name="fk_role"))
    @Cascade( { org.hibernate.annotations.CascadeType.ALL})
    private Set<Role> roles;
    
    /**
	 * Permissions of the user
	 */
    @ManyToMany(fetch=FetchType.LAZY)
	@JoinTable( name="kernely_user_permissions",
				joinColumns=@JoinColumn(name="fk_user"),
				inverseJoinColumns=@JoinColumn(name="fk_permission"))
    @Cascade( { org.hibernate.annotations.CascadeType.ALL})
    private Set<Permission> permissions;
	
    /**
     * Default constructor, fill user with empty values.
     */
    public User(){
    	this.id=0;
    	this.login="";
    	this.password="";
    	this.enabled=0;
    	this.groups = new HashSet<Group>();
    	this.permissions = new HashSet<Permission>();
    	this.roles = new HashSet<Role>();
    }
    
	/**
	 * @return the permissions
	 */
	public final Set<Permission> getPermissions() {
		return permissions;
	}

	/**
	 * @param permissions the permissions to set
	 */
	public final void setPermissions(Set<Permission> permissions) {
		this.permissions = permissions;
	}

	/**
	 * @return the roles
	 */
	public final Set<Role> getRoles() {
		return roles;
	}
	
	/**
	 * Return all user's roles, even his groups' roles
	 * @return : A set containing all his roles
	 */
	public final Set<Role> getAllRoles(){
		Set<Role> allRoles = new HashSet<Role>();
		allRoles.addAll(roles);
		for(Group g : groups){
			allRoles.addAll(g.getRoles());
		}
		return allRoles;
	}
	
	/**
	 * Return all user's permissions, even his groups' permissions
	 * @return : A set containing all his permissions
	 */
	public final Set<Permission> getAllPermissions(){
		Set<Permission> allPermissions = new HashSet<Permission>();
		allPermissions.addAll(permissions);
		for(Group g : groups){
			allPermissions.addAll(g.getPermissions());
		}
		return allPermissions;
	}

	/**
	 * @param roles the roles to set
	 */
	public final void setRoles(Set<Role> roles) {
		this.roles = roles;
	}

	/**
	 * Get the authorization to acces to the application
	 * @return the authorization : 1 to access, 0 else
	 */
	public final int getEnabled() {
		return enabled;
	}

	/**
	 * Set the authorization to access to the application
	 * @param enabled : the authorization : 1 to access, 0 else
	 */
	public final void setEnabled(int enabled) {
		this.enabled = enabled;
	}

	/**
	 * Retrieve the User's login
	 * @return the user's login
	 */
	public final String getLogin() {
		return login;
	}

	/**
	 * Set the user's login
	 * @param login : the user's login
	 */
	public final void setLogin(String login) {
		this.login = login;
	}

	/**
	 * Retrieve the user's password
	 * @return the user's password
	 */
	public final String getPassword() {
		return password;
	}

	/**
	 * Set the user's password
	 * @param password : the user's password
	 */
	public final void setPassword(String password) {
		this.password = password;
	}

	/**
	 * Retrieve the user's id
	 * @return the user's id
	 */
	public final int getId() {
		return id;
	}

	/**
	 * Set the user's id
	 * @param id : the user's id
	 */
	public final void setId(int id) {
		this.id = id;
	}
	
	
	/**
	 * Gets the groups of the user
	 * @return the groups
	 */
	public final Set<Group> getGroups() {
		return groups;
	}

	/**
	 * Sets the groups of the user
	 * @param groups the groups to set
	 */
	public final void setGroups(Set<Group> groups) {
		this.groups = groups;
	}
	
	/**
	 * Verify that the user has one of the role list
	 * @param : the list of roles
	 * @return boolean : true if the user has one of these roles
	 */
	public final boolean hasOneOf(String ... rolesList){
		for(Role r : this.getAllRoles()){
			for(String s : rolesList){
				if(r.getName().equals(s)){
					return true;
				}
			}
		}
		return false;
	}
	
	/**
	 * Verify that the user has one of the roles in the set.
	 * @param the set of roles
	 * @return boolean true if the user has one of these roles
	 */
	public final boolean hasOneOf(Set<String> rolesSet){
		for(Role r : this.getAllRoles()){
			for(String s : rolesSet){
				if(r.getName().equals(s)){
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public final int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		result = prime * result + ((login == null) ? 0 : login.hashCode());
		result = prime * result
				+ ((password == null) ? 0 : password.hashCode());
		return result;
	}

	/** 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public final boolean equals(Object obj) {
		if (this == obj){
			return true;
		}
		if (obj == null){
			return false;
		}
		if (getClass() != obj.getClass()){
			return false;
			
		}
		User other = (User) obj;
		if (id != other.id){
			return false;
		}
		if (login == null) {
			if (other.login != null){
				return false;
			}
		} else if (!login.equals(other.login)){
			return false;
		}
		if (password == null) {
			if (other.password != null){
				return false;
			}
		} else if (!password.equals(other.password)){
			return false;
		}
		return true;
	}



}
